package com.wiscamp.ninechapters.tags.infrastructure.repositories;

import com.wiscamp.ninechapters.common.core.RandomHelper;
import com.wiscamp.ninechapters.tags.domain.models.KnowledgePoint;
import com.wiscamp.ninechapters.tags.domain.models.KnowledgePointRelation;
import com.wiscamp.ninechapters.tags.domain.models.KnowledgeTreePoint;
import com.wiscamp.ninechapters.tags.domain.repositories.KnowledgePointRepository;
import com.wiscamp.ninechapters.tags.enums.PointRelationTypes;
import com.wiscamp.ninechapters.tags.infrastructure.converters.KnowledgePointConverter;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.KnowledgePointMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.KnowledgePointRelationMapper;
import com.wiscamp.ninechapters.tags.infrastructure.mappers.KnowledgePointViewMapper;
import com.wiscamp.ninechapters.tags.infrastructure.po.KnowledgePointPO;
import com.wiscamp.ninechapters.tags.infrastructure.po.KnowledgePointRelationPO;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.stream.Collectors;

/**
 * 学科知识点仓储对象
 */
@Repository
public class KnowledgePointRepositoryImpl extends BaseRepository implements KnowledgePointRepository {

    private final KnowledgePointMapper knowledgePointMapper;

    private final KnowledgePointViewMapper knowledgePointViewMapper;

    private final KnowledgePointRelationMapper knowledgePointRelationMapper;

    public KnowledgePointRepositoryImpl(KnowledgePointMapper knowledgePointMapper, KnowledgePointViewMapper knowledgePointViewMapper, KnowledgePointRelationMapper knowledgePointRelationMapper) {
        this.knowledgePointMapper = knowledgePointMapper;
        this.knowledgePointViewMapper = knowledgePointViewMapper;
        this.knowledgePointRelationMapper = knowledgePointRelationMapper;
    }


    // region subject point
    private KnowledgePointPO getKnowledgePointPO(long id) {
        return PersistenceUtils.findById(id, knowledgePointMapper::findById);
    }

    @HandleDataException
    public KnowledgePoint getKnowledgePoint(@Min(0) long pointId) {
        return PersistenceUtils.findEntityById(pointId, knowledgePointViewMapper::findById,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public KnowledgePoint getKnowledgePointByCode(@NotBlank String pointCode, @Min(0) long subjectId) {
        return PersistenceUtils.getEntityByTextAndId(pointCode, subjectId, knowledgePointViewMapper::findByPointCode,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public KnowledgePoint getFirstKnowledgePointByName(@NotBlank String pointName, @Min(0) long subjectId) {
        var points = getKnowledgePointsByName(pointName, subjectId);
        var optionalPoint = points.stream().findFirst();
        return optionalPoint.orElse(null);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByName(@NotBlank String pointName, @Min(0) long subjectId) {
        return PersistenceUtils.getEntitiesByTextAndId(pointName, subjectId, knowledgePointViewMapper::findListByPointName,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByNameContains(@NotBlank String name, @Min(0) long subjectId) {
        var tempName = "%" + name.trim() + "%";
        return PersistenceUtils.getEntitiesByTextAndId(tempName, subjectId, knowledgePointViewMapper::findListByNameContains,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsBySubject(@Min(0) long subjectId) {
        return PersistenceUtils.getEntitiesById(subjectId, knowledgePointViewMapper::findListBySubject,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<Long> getKnowledgePointIdsBySubject(@Min(0) long subjectId) {
        var points = getKnowledgePointsBySubject(subjectId);
        return points.stream().map(KnowledgePoint::getPointId).collect(Collectors.toList());
    }

    @HandleDataException
    public Collection<KnowledgePoint> getKnowledgePointsByIds(@NotBlank Collection<Long> pointIds) {
        return PersistenceUtils.getEntitiesByIds(pointIds, knowledgePointViewMapper::findAllById,
                KnowledgePointConverter.Instance::toKnowledgePointByView);
    }

    @HandleDataException
    public Collection<KnowledgePoint> getRandomKnowledgePoints(@Min(0) long subjectId, int quantity) {
        var pointIds = getKnowledgePointIdsBySubject(subjectId);
        if (pointIds.isEmpty()) return new ArrayList<>();
        var randomPointIds = RandomHelper.getRandomCollection(pointIds, quantity);
        return getKnowledgePointsByIds(randomPointIds);
    }

    @HandleDataException
    public KnowledgePoint saveKnowledgePoint(@NotNull KnowledgePoint point) {
        var newId = point.getPointId();
        if (point.getPointId() <= 0) {
            var newPoint = PersistenceUtils.createEntity(point, knowledgePointMapper::saveAndFlush,
                    KnowledgePointConverter.Instance::toKnowledgePointPO, KnowledgePointConverter.Instance::toKnowledgePoint);
            newId = newPoint.getPointId();
        } else {
            var existedPO = getKnowledgePointPO(point.getPointId());
            if (existedPO == null) existedPO = new KnowledgePointPO();
            PersistenceUtils.updateEntity(point, existedPO, knowledgePointMapper::saveAndFlush,
                    KnowledgePointConverter.Instance::toKnowledgePointPO, KnowledgePointConverter.Instance::toKnowledgePoint);
        }
        return getKnowledgePoint(newId);
    }

    // endregion

    //region relation
    private KnowledgePointRelationPO getKnowledgePointRelationPO(@Min(0) long id) {
        return PersistenceUtils.findById(id, knowledgePointRelationMapper::findById);
    }

    @HandleDataException
    public KnowledgePointRelation getKnowledgePointRelation(@Min(0) long id) {
        return PersistenceUtils.findEntityById(id, knowledgePointRelationMapper::findById,
                KnowledgePointConverter.Instance::toKnowledgePointRelation);
    }

    @HandleDataException
    public KnowledgePointRelation getKnowledgePointRelation(@Min(0) long sourceId, @Min(0) long targetId, int relationTypeId) {
        var relationPO = knowledgePointRelationMapper.findRelationBySourceAndTarget(sourceId, targetId, relationTypeId);
        return KnowledgePointConverter.Instance.toKnowledgePointRelation(relationPO);
    }

    @HandleDataException
    public KnowledgePointRelation saveKnowledgePointRelation(@NotNull KnowledgePointRelation relation) {
        var newId = relation.getId();
        if (relation.getId() <= 0) {
            var newRelation = PersistenceUtils.createEntity(relation, knowledgePointRelationMapper::saveAndFlush,
                    KnowledgePointConverter.Instance::toKnowledgePointRelationPO, KnowledgePointConverter.Instance::toKnowledgePointRelation);
            newId = newRelation.getId();
        } else {
            var existedPO = getKnowledgePointRelationPO(relation.getId());
            if (existedPO == null) existedPO = new KnowledgePointRelationPO();
            PersistenceUtils.updateEntity(relation, existedPO, knowledgePointRelationMapper::saveAndFlush,
                    KnowledgePointConverter.Instance::toKnowledgePointRelationPO, KnowledgePointConverter.Instance::toKnowledgePointRelation);
        }
        return getKnowledgePointRelation(newId);
    }

    @HandleDataException
    public Collection<KnowledgePointRelation> getKnowledgePointRelationsBySource(long sourceId) {
        return PersistenceUtils.getEntitiesById(sourceId, knowledgePointRelationMapper::findRelationsBySource,
                KnowledgePointConverter.Instance::toKnowledgePointRelation);
    }

    @HandleDataException
    public Collection<KnowledgePointRelation> getNonAggregationRelationsBySource(long sourceId) {
        var relations = getKnowledgePointRelationsBySource(sourceId);
        return relations.stream().filter(r -> r.getRelationTypeId() != PointRelationTypes.AGGREGATION.getValue()).collect(Collectors.toList());
    }

    @HandleDataException
    public KnowledgePointRelation getKnowledgePointRelation(long sourceId, long targetId, PointRelationTypes relationType) {
        var relationPO = knowledgePointRelationMapper.findRelationBySourceAndTarget(sourceId, targetId, relationType.getValue());
        return KnowledgePointConverter.Instance.toKnowledgePointRelation(relationPO);
    }

    @HandleDataException
    public void deleteKnowledgePointRelation(@Min(0) long id) {
        PersistenceUtils.delete(id, knowledgePointRelationMapper::deleteById);
    }

    @HandleDataException
    public Collection<Long> getKnowledgePointIdsByParent(@NotBlank Collection<Long> parentIds) {
        var pointIds = getChildKnowledgePointIds(parentIds);
        pointIds.addAll(parentIds);
        return pointIds;
    }

    @HandleDataException
    public Collection<Long> getChildKnowledgePointIds(@NotEmpty Collection<Long> parentIds) {
        Collection<Long> ids = new HashSet<>();
        Collection<Long> temp = parentIds;
        for (int i = 1; i <= 15; i++) {
            Collection<Long> subPointIds = knowledgePointRelationMapper.findPointIdsByRelationType(temp, PointRelationTypes.AGGREGATION.getValue());
            if (subPointIds.size() > 0) {
                ids.addAll(subPointIds);
                temp = subPointIds;
            } else {
                break;
            }
        }
        // 为避免in语句项目过多破环sql语句，限制只随机100个子知识点
        // ids = RandomHelper.getRandomCollection(ids, 30);
        return ids;
    }
    //endregion

    // region knowledgePoint tree

    /**
     * 根据学科编号获取树形结构知识点信息 （用于知识树编辑）
     *
     * @param subjectId
     * @return
     */
    @HandleDataException
    public Collection<KnowledgeTreePoint> getKnowledgeTreePointsBySubject(@Min(0) long subjectId) {
        return knowledgePointViewMapper.findKnowledgeTreePointsBySubject(subjectId, PointRelationTypes.AGGREGATION.getValue());
    }


    @HandleDataException
    public Collection<KnowledgeTreePoint> getKnowledgeTreePointsByParent(@Min(0) long subjectId, @Min(0) long parentId) {
        Collection<Long> ids = new ArrayList<>();
        ids.add(parentId);
        Collection<Long> points = getKnowledgePointIdsByParent(ids);
        return getKnowledgeTreePointsByIds(subjectId, points);
    }

    /**
     * 根据一组知识点获取下级树形结构知识点信息 （用于知识树编辑）
     *
     * @param pointIds
     * @return
     */
    @HandleDataException
    public Collection<KnowledgeTreePoint> getKnowledgeTreePointsByIds(@Min(0) long subjectId, Collection<Long> pointIds) {
        return knowledgePointViewMapper.findKnowledgeTreePointsByIds(subjectId, pointIds, PointRelationTypes.AGGREGATION.getValue());
    }
    // endregion
}
